﻿using System;
using System.Collections.Generic;
using System.Text;
using DreamShield.Deployment.Installers.DreamWorker.PluginsInterfaces;
using DreamShield.Deployment.Assistants.DreamWizard.WizardInterfaces.StandardWizard;
using DreamShield.Deployment.Wizards;
using DreamShield.Deployment.Installers.DreamWorker;
using DreamShield.Deployment.Wizards.Workflow;
using System.Windows.Forms;
using System.ComponentModel;

namespace DreamShield.Deployment.Miles.CustomPage
{
    /// <summary>
    /// Plugin qui permet l'affichage d'une page personnalisée
    /// </summary>
    public class CustomPagePlugin : BaseMilesPlugin
    {
        /// <summary>
        /// Intègre une nouvelle page personnalisée à l'assistant
        /// </summary>
        /// <param name="server">Serveur Miles en cours</param>
        /// <param name="position">Id de la page par rapport à laquelle placer celle ci. Pour
        /// avoir le nom des pages par défauts, consultez <see cref="WizardPagesID"/> pour obtenir
        /// le nom des pages par défaut</param>
        /// <param name="appendAfter">Indique si la page personnalisée doit être rajoutée après la page position,
        /// sinon elle est insérée avant</param>
        /// <param name="pageId">id de la page générée. Si id est vide, un id aléatoire est généré</param>
        /// <param name="pageUI">Contrôle qui sera affiché en tant que page personnalisée</param>
        public CustomPagePlugin(MilesRootServer server, string position, bool appendAfter, string pageId, Control pageUI) : base(server)
        {
            IsRunning = false;

            if (String.IsNullOrEmpty(position))
                throw new ArgumentNullException("position");

            if (pageUI == null)
                throw new ArgumentNullException("pageUI");

            if (String.IsNullOrEmpty(pageId))
                pageId = Guid.NewGuid().ToString();

            NativePage = new NativeWizardPage(pageUI);

            NativePage._Id = pageId;

            NativePlugin = new NativeWizardPagePlugin();
            NativePlugin.DesiredPage = position;
            NativePlugin.InsertPageAfter = appendAfter;
            NativePlugin.Page = NativePage;

            _NativePluginType = StandardWizardProvider.StandardWizardProvider_PluginsGuid;
            _NativePluginImplementation = NativePlugin;

            ConnectNativePage();

            InitProperties();
        }

        /// <summary>
        /// Initialise les propriétés par défaut
        /// </summary>
        protected virtual void InitProperties()
        {
            CanGoBack = true;
            CanGoNext = true;
            CanCancel = true;

            CanShow = true;

            ShowForCustom = false;
            ShowForInstall = true;
            ShowForUninstall = false;
            ShowForUpdate = false;
        }

        /// <summary>
        /// Connecte les évènements de la page native
        /// </summary>
        protected virtual void ConnectNativePage()
        {
            NativePage.OnAcceptBack += new AcceptPageResultEvent(NativePage_OnAcceptBack);
            NativePage.OnAcceptCancel += new AcceptPageResultEvent(NativePage_OnAcceptCancel);
            NativePage.OnAcceptNext += new AcceptPageResultEvent(NativePage_OnAcceptNext);

            NativePage.OnBeforeStart += new EventHandler(NativePage_OnBeforeStart);
            NativePage.OnFinishedGoneTrue += new EventHandler(NativePage_OnFinishedGoneTrue);
        }

        /// <summary>
        /// Lorsque le status Finished le la page native passe à true
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void NativePage_OnFinishedGoneTrue(object sender, EventArgs e)
        {
            if (IsRunning)
            {
                IsRunning = false;

                if (OnPageEnd != null)
                    OnPageEnd(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Avant de démarrage de la page
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void NativePage_OnBeforeStart(object sender, EventArgs e)
        {
            if (OnPageBegin != null)
                OnPageBegin(this, EventArgs.Empty);

            IsRunning = true;
        }

        /// <summary>
        /// Lorsque l'action "Suivant" est déclenchée
        /// </summary>
        /// <param name="accept"></param>
        protected virtual void NativePage_OnAcceptNext(ref bool accept)
        {
            if (OnNextRequest != null)
            {
                CancelEventArgs args = new CancelEventArgs(!accept);
                OnNextRequest(this, args);
                accept = !args.Cancel;
            }
        }

        /// <summary>
        /// Lorsque l'action "Annuler" est déclenchée
        /// </summary>
        /// <param name="accept"></param>
        protected virtual void NativePage_OnAcceptCancel(ref bool accept)
        {
            if (OnCancelRequest != null)
            {
                CancelEventArgs args = new CancelEventArgs(!accept);
                OnCancelRequest(this, args);
                accept = !args.Cancel;
            }
        }

        /// <summary>
        /// Lorsque l'action "Précédent" est déclenchée
        /// </summary>
        /// <param name="accept"></param>
        protected virtual void NativePage_OnAcceptBack(ref bool accept)
        {
            if (OnBackRequest != null)
            {
                CancelEventArgs args = new CancelEventArgs(!accept);
                OnBackRequest(this, args);
                accept = !args.Cancel;
            }
        }

        #region Liaison avec DreamShield

        #region Classes de liaison

        /// <summary>
        /// Page native utilisée pour la liaison avec le wizard DreamShield
        /// </summary>
        protected class NativeWizardPage : BaseWizardPage
        {
            /// <summary>
            /// Infrastucture
            /// </summary>
            protected virtual Control Ctrl { get; set; }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public NativeWizardPage(Control ctrl) : base("")
            {
                Ctrl = ctrl;

                AllowBack = true;
                AllowCancel = true;
                AllowNext = true;
                AllowShow = true;
            }

            #region IWizardPage Membres

            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool AllowBack { get; set; }
            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool AllowCancel { get; set; }
            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool AllowNext { get; set; }
            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool AllowShow { get; set; }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public override bool CanBack
            {
                get { return AllowBack; }
            }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public override bool CanCancel
            {
                get { return AllowCancel; }
            }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public override bool CanNext
            {
                get { return AllowNext; }
            }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public override bool CanShow
            {
                get { return AllowShow; }
            }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual string _Id { get; set; }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public override string Id
            {
                get { return _Id; }
            }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public override Control PageControl
            {
                get { return Ctrl; }
            }

            #endregion

            /// <summary>
            /// Infrastucture
            /// </summary>
            public event EventHandler OnBeforeStart;

            /// <summary>
            /// Infrastucture
            /// </summary>
            public override bool StartPage()
            {
                if (OnBeforeStart != null)
                    OnBeforeStart(this, EventArgs.Empty);

                return base.StartPage();
            }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public event EventHandler OnFinishedGoneTrue;

            /// <summary>
            /// Infrastucture
            /// </summary>
            public override bool Finished
            {
                get
                {
                    return base.Finished;
                }
                protected set
                {
                    base.Finished = value;
                    
                    if (value)
                    {
                        if (OnFinishedGoneTrue != null)
                            OnFinishedGoneTrue(this, EventArgs.Empty);
                    }
                }
            }
        }

        /// <summary>
        /// Classe de liaison avec le framework de plugin DreamShield
        /// </summary>
        protected class NativeWizardPagePlugin : BaseStandardWizardProvider_Plugin
        {
            /// <summary>
            /// Infrastucture
            /// </summary>
            public NativeWizardPagePlugin()
            {
                Mode = null;
            }

            #region Propriétés générales
            IBaseWizardForm wizard = null;

            /// <summary>
            /// Wizard actuel
            /// </summary>
            public virtual IBaseWizardForm Wizard
            {
                get { return wizard; }
            }

            /// <summary>
            /// Indique le mode dans lequel l'assistant est : permet de savoir si on se trouve
            /// pendant une installation ou une désinstallation (la page n'étant affichée que pendant l'installation)
            /// </summary>
            public virtual WorkerActionId? Mode { get; set; }
            #endregion

            /// <summary>
            /// Appelé au début de la construction de l'assistant d'installation
            /// </summary>
            /// <param name="actionId">Mode (installation/désinstallation) dans lequel l'installeur est</param>
            public override void BuildWizard_Begin(WorkerActionId actionId)
            {
                base.BuildWizard_Begin(actionId);

                Mode = actionId;
            }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public EventHandler OnPageFound;
            /// <summary>
            /// Infrastucture
            /// </summary>
            public EventHandler OnPageNotFound;

            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool ShowForCustom { get; set; }
            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool ShowForInstall { get; set; }
            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool ShowForUninstall { get; set; }
            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool ShowForUpdate { get; set; }

            /// <summary>
            /// Appelé après la construction de l'assistant d'installation.
            /// Cette méthode permet de rajouter la page personnalisée après la page d'acceuil (WelcomePage)
            /// </summary>
            /// <param name="actionId">Mode (installation/désinstallation) dans lequel l'installeur est</param>
            public override void BuildWizard_End(WorkerActionId actionId)
            {
                base.BuildWizard_End(actionId);

                wizard = Provider.InstallerForm;

                bool execute_show = false;

                if (!Mode.HasValue)
                    throw new InvalidOperationException("Aucun mode définis pour le wizard");
                else
                {
                    switch (Mode.Value)
                    {
                        case WorkerActionId.Custom: { execute_show = ShowForCustom; break; }
                        case WorkerActionId.Install: { execute_show = ShowForInstall; break; }
                        case WorkerActionId.Uninstall: { execute_show = ShowForUninstall; break; }
                        case WorkerActionId.Update: { execute_show = ShowForUpdate; break; }
                        default:
                            {
                                throw new NotSupportedException(String.Format("Mode d'assistant inconnu : {0}", Mode.Value.ToString()));
                            }
                    }
                }

                if (execute_show)
                {
                    WorkflowSequencer workflow = Wizard.GuiPagesSequencer;

                    /* on cherche la page  */
                    if (FindDesiredPage(workflow))
                    {
                        /* les pages ont été ajoutés si nécéssaire */
                        if (OnPageFound != null)
                            OnPageFound(this, EventArgs.Empty);
                    }
                    else
                    {
                        /* pas de page trouvée :s
                         * */
                        if (OnPageNotFound != null)
                            OnPageNotFound(this, EventArgs.Empty);
                    }
                }
            }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual string DesiredPage { get; set; }

            /// <summary>
            /// Cette fonction recherche la page dans les pages de l'assistant,
            /// et rajoute la page personnalisée juste après
            /// </summary>
            /// <param name="root"></param>
            /// <returns></returns>
            protected virtual bool FindDesiredPage(WorkflowSequencer root)
            {
                /*
                 * Il faut énumérer tous les éléments de la séquence.
                 * Les éléments parcourus n'ont pas vraiment d'importance, seule
                 * la page d'acceuil compte
                 * */
                WorkflowSequencer m_sequencer = null;
                foreach (IWorkflowSingleton singleton in root.Flows)
                {
                    /* pour rajouter la page personnalisée après une autre page que Welcome,
                     * il suffit de remplacer DreamShield.Deployment.Wizards.StdWizardPageID.WelcomePage
                     * par un autre champ de la classe DreamShield.Deployment.Wizards.StdWizardPageID
                     * qui contient toutes les pages standard
                     * */
                    if (singleton.Id.Equals(DesiredPage, StringComparison.OrdinalIgnoreCase))
                    {
                        ProcessPage(root, singleton);
                        return true;
                    }

                    m_sequencer = singleton as WorkflowSequencer;

                    if (m_sequencer != null)
                    {
                        if (FindDesiredPage(m_sequencer))
                            return true;

                    }
                }

                return false;
            }

            /// <summary>
            /// Page personnalisée qui sera affichée
            /// </summary>
            public virtual IWizardPage Page { get; set; }

            /// <summary>
            /// Infrastucture
            /// </summary>
            public virtual bool InsertPageAfter { get; set; }

            /// <summary>
            /// Cette fonction est Appelée lorsque la page d'acceuil a été trouvée
            /// et y rajoute la page personnalisée
            /// </summary>
            /// <param name="root"></param>
            /// <param name="singleton"></param>
            private void ProcessPage(WorkflowSequencer root, IWorkflowSingleton singleton)
            {
                WorkflowBasicPageSequence page_sample = new WorkflowBasicPageSequence(
                    Wizard, Page, Page.Id);

                root.Flows.Insert(
                    InsertPageAfter ? root.Flows.IndexOf(singleton) + 1 : root.Flows.IndexOf(singleton), 
                    page_sample);
            }
        }
        #endregion

        #region Propriété de liaison

        /// <summary>
        /// Plugin native
        /// </summary>
        protected virtual NativeWizardPagePlugin NativePlugin { get; set; }

        /// <summary>
        /// Page native DreamShield
        /// </summary>
        protected virtual NativeWizardPage NativePage { get; set; }

        #endregion

        #endregion

        #region Evènements

        /// <summary>
        /// Se produit avant l'appel de la page. Cette page sera affichée
        /// si CanShow vaut true
        /// </summary>
        public event EventHandler OnPageBegin;

        /// <summary>
        /// Se produit après avoir affiché une page
        /// </summary>
        public event EventHandler OnPageEnd;

        /// <summary>
        /// Demande de confirmation. Permet d'indiquer si l'action Suivant peut être exécutée
        /// </summary>
        public event CancelEventHandler OnNextRequest;
        /// <summary>
        /// Demande de confirmation. Permet d'indiquer si l'action Précédent peut être exécutée
        /// </summary>
        public event CancelEventHandler OnBackRequest;
        /// <summary>
        /// Demande de confirmation. Permet d'indiquer si l'action Annuler peut être exécutée
        /// </summary>
        public event CancelEventHandler OnCancelRequest;

        #endregion

        #region Contrôle publique

        /// <summary>
        /// Indique si la page est en cours d'affichage
        /// </summary>
        public virtual bool IsRunning { get; protected set; }

        /// <summary>
        /// Indique ou défini si la page personnalisée doit être affichée dans l'assistant
        /// </summary>
        public virtual bool CanShow
        {
            get { return NativePage.AllowShow; }
            set { NativePage.AllowShow = value; }
        }

        /// <summary>
        /// Indique ou défini si le bouton "Suivant" est accessible
        /// </summary>
        public virtual bool CanGoNext
        {
            get { return NativePage.AllowNext; }
            set
            {
                NativePage.AllowNext = value;
                NativePage.ActualizeButtonStates();
            }
        }

        /// <summary>
        /// Indique ou défini si le bouton "Précédent" est accessible
        /// </summary>
        public virtual bool CanGoBack
        {
            get { return NativePage.AllowBack; }
            set
            {
                NativePage.AllowBack = value;
                NativePage.ActualizeButtonStates();
            }
        }

        /// <summary>
        /// Indique ou défini si le bouton "Annuler" est accessible
        /// (ainsi que la fermeture de l'assistant par la croix de la fenêtre)
        /// </summary>
        public virtual bool CanCancel
        {
            get { return NativePage.AllowCancel; }
            set
            {
                NativePage.AllowCancel = value;
                NativePage.ActualizeButtonStates();
            }
        }

        /// <summary>
        /// Indique ou défini si la page doit être affiché si le mode de l'assistant est "Custom"
        /// Par défaut, la page n'est affichée que pour l'installation
        /// </summary>
        public virtual bool ShowForCustom
        {
            get { return NativePlugin.ShowForCustom; }
            set { NativePlugin.ShowForCustom = value; }
        }

        /// <summary>
        /// Indique ou défini si la page doit être affiché si le mode de l'assistant est "Install"
        /// Par défaut, la page n'est affichée que pour l'installation
        /// </summary>
        public virtual bool ShowForInstall
        {
            get { return NativePlugin.ShowForInstall; }
            set { NativePlugin.ShowForInstall = value; }
        }

        /// <summary>
        /// Indique ou défini si la page doit être affiché si le mode de l'assistant est "Uninstall"
        /// Par défaut, la page n'est affichée que pour l'installation
        /// </summary>
        public virtual bool ShowForUninstall
        {
            get { return NativePlugin.ShowForUninstall; }
            set { NativePlugin.ShowForUninstall = value; }
        }

        /// <summary>
        /// Indique ou défini si la page doit être affiché si le mode de l'assistant est "Update"
        /// Par défaut, la page n'est affichée que pour l'installation
        /// </summary>
        public virtual bool ShowForUpdate
        {
            get { return NativePlugin.ShowForUpdate; }
            set { NativePlugin.ShowForUpdate = value; }
        }

        /// <summary>
        /// Execute l'action du bouton "Suivant"
        /// </summary>
        /// <returns>true si l'opération s'est bien déroulée, false si elle a été annulée</returns>
        public virtual bool ExecuteNext()
        {
            return NativePage.AcceptNext();
        }

        /// <summary>
        /// Execute l'action du bouton "Précédent"
        /// </summary>
        /// <returns>true si l'opération s'est bien déroulée, false si elle a été annulée</returns>
        public virtual bool ExecuteBack()
        {
            return NativePage.AcceptBack();
        }

        /// <summary>
        /// Execute l'action du bouton "Annuler"
        /// </summary>
        /// <returns>true si l'opération s'est bien déroulée, false si elle a été annulée</returns>
        public virtual bool ExecuteCancel()
        {
            return NativePage.AcceptCancel();
        }

        #endregion
    }
}
